﻿using System;
using System.Collections.Generic;
using System.Linq;
using KadalaSpree.XCore;
using Zeta.Common;
using Zeta.TreeSharp;
using Action = Zeta.TreeSharp.Action;

namespace KadalaSpree.XCore
{

    public class XFlow
    {
        /// <summary>
        /// Behavior should run or not
        /// </summary>
        public bool IsDone { get; set; }

        /// <summary>
        /// Reset to default state
        /// </summary>
        internal bool InternalReset()
        {
            IsDone = false;
            _runDelegates.Clear();
            return true;
        }

        //public delegate void RunOnceDelegate();
        private readonly List<Delegate> _runDelegates = new List<Delegate>();

        /// <summary>
        /// Runs a function once
        /// </summary>
        /// <param name="func">delegate to execute</param>
        /// <returns>success if function was run</returns>
        public RunStatus RunOnce(Delegate func)
        {
            if (!_runDelegates.Any(d => d.Equals(func)))
            {
                func.DynamicInvoke();
                _runDelegates.Add(func);
                return RunStatus.Success;
            }
            return RunStatus.Failure;
        }

        public bool IsStarted = false;

        public Composite Update(IsDoneDelegate condition, OnDoneDelegate onDone = null, OnStartDelegate onStart = null)
        {
            return new Action(ret =>
            {
                //if (IsDone && IsStarted)
                //{
                //    // fire end function
                //    if (onDone != null) onDone();
                //    IsStarted = false;
                //}

                // If condition is true, we're done
                if (condition())
                {
                    X.Logger.Instance.Log("IsFinished IsDone={0} IsStarted={1}", IsDone, IsStarted);           
                    IsDone = true;

                    if (IsStarted)
                    {
                        // fire end function
                        if (onDone != null) onDone();
                        _runDelegates.Clear();
                        IsStarted = false;
                    }

                    //block prioritySelector
                    return RunStatus.Success;
                }

                X.Logger.Instance.Log("!IsFinished IsDone={0} IsStarted={1}", IsDone, IsStarted);  

                // Fire start function
                if (onStart != null)
                {
                    IsStarted = true;
                    RunOnce(onStart);
                }  
              
                // allow priority selector to continue
                return RunStatus.Failure;                
            });
        }

  

        public delegate bool IsDoneDelegate();
        public delegate void OnDoneDelegate();
        public delegate void OnStartDelegate();

        /// <summary>
        /// Action that returns RunStatus.Success
        /// </summary>
        public Action AlwaysSucceed
        {
            get { return new Action(ret => RunStatus.Success); }
        }

        /// <summary>
        /// Action that returns RunStatus.Failure
        /// </summary>
        public Action AlwaysFail
        {
            get { return new Action(ret => RunStatus.Failure); }
        }

        /// <summary>
        /// Sets current behavior to end, logs message and returns success
        /// </summary>
        /// <param name="message">message format</param>
        /// <param name="args">message format args</param>
        public RunStatus EndSuccessMessage(string message, params object[] args)
        {
            if (!IsDone || !SuppressLoggingWhenDone) X.Logger.Instance.Log(message, args);
            IsDone = true;
            return RunStatus.Success;
        }
     
        /// <summary>
        /// Sets current behavior to end, logs message and returns failure
        /// </summary>
        /// <param name="message">message format</param>
        /// <param name="args">message format args</param>
        public RunStatus EndFailureMessage(string message, params object[] args)
        {
            if (!IsDone || !SuppressLoggingWhenDone) X.Logger.Instance.Log(message, args);
            IsDone = true;
            return RunStatus.Failure;
        }

        /// <summary>
        /// Sets current behavior to end, logs message and returns false
        /// </summary>
        /// <param name="message">message format</param>
        /// <param name="args">message format args</param>
        public bool EndFalseMessage(string message, params object[] args)
        {
            if (!IsDone || !SuppressLoggingWhenDone) X.Logger.Instance.Log(message, args);
            IsDone = true;
            return false;
        }

        /// <summary>
        /// Sets current behavior to end, logs message and returns false
        /// </summary>
        /// <param name="message">message format</param>
        /// <param name="args">message format args</param>
        public bool EndFalse()
        {
            if (!IsDone || !SuppressLoggingWhenDone) IsDone = true;
            return false;
        }

        /// <summary>
        /// Sets current behavior to end, logs message and returns true
        /// </summary>
        /// <param name="message">message format</param>
        /// <param name="args">message format args</param>
        public bool EndTrueMessage(string message, params object[] args)
        {
            if (!IsDone || !SuppressLoggingWhenDone) X.Logger.Instance.Log(message, args);
            IsDone = true;
            return true;
        }

        /// <summary>
        /// Returns false with a log message
        /// </summary>
        /// <param name="message">message format</param>
        /// <param name="args">message format args</param>
        public bool ContinueFalseMessage(string message, params object[] args)
        {
            if (!IsDone || !SuppressLoggingWhenDone) X.Logger.Instance.Log(message, args);
            return false;
        }

        /// <summary>
        /// Converts a runstatus result to boolean
        /// </summary>
        public bool BooleanResult(RunStatus status)
        {
            return status == RunStatus.Success;
        }

        /// <summary>
        /// Returns the opposite for Success and Failure
        /// </summary>
        public RunStatus Reverse (RunStatus status)
        {
            return (status == RunStatus.Running) ? status : (status == RunStatus.Success) ? RunStatus.Failure : RunStatus.Success;
        }


        public bool SuppressLoggingWhenDone { get; set; }

        /// <summary>
        /// Sets flow to IsDone=True
        /// <para>Returns Success</para>
        /// </summary>
        public Action Done()
        {
            return new Action(ret => IsDone = true);
        }

        /// <summary>
        /// Resets flow to IsDone=False and clears RunOnce collection
        /// <para>Returns Success</para>
        /// </summary>
        public Action Reset()
        {
            return new Action(ret => InternalReset());
        }
    }
}